package com.ueboot.weishop.service.weixin.impl;

import com.alibaba.fastjson.JSON;
import com.ueboot.core.utils.HttpClientUtil;
import com.ueboot.core.utils.WXUtil;
import com.ueboot.core.utils.XMLUtil;
import com.ueboot.weishop.service.weixin.*;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.net.ssl.SSLContext;
import java.io.*;
import java.security.KeyStore;
import java.util.*;

/**
 * Created by yangkui on 15/11/4.
 * @author yangkui
 * 微信接口调用服务类,微信消息发送，下订单
 */
@Service
public class WeixinServiceImpl implements WeiXinService {
    private static Logger logger = LoggerFactory.getLogger(WeixinServiceImpl.class);
    @Value(value = "${weixin.config.certPath}")
    private String wxCertPath;

    @Value(value = "${weixin.config.appId}")
    private String wxAppId;

    @Value(value = "${weixin.config.appSecret}")
    private String wxAppSecret;

    @Value(value = "${weixin.config.appKey}")
    private String wxAppKey;

    @Value(value = "${weixin.config.mchId}")
    private String wxMchId;
    @Value(value = "${weixin.config.returnUrl}")
    private String wxPayReturnUrl;


    public static final String WEIXIN_ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
    public static final String WEIXIN_SEND_MESSAGE = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=";
    /*微信支付统一下单*/
    public static final String WEIXIN_PAY_UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    /*微信退款统一撤单*/
    public static final String WEIXIN_REFUND_ORDER = "https://api.mch.weixin.qq.com/secapi/pay/refund";
    /*微信发送普通红包*/
    public static final String WEIXIN_RED_PACK = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
    /*微信下载对账单*/
    public static final String WEIXIN_DOWNLOAD_BILL = "https://api.mch.weixin.qq.com/pay/downloadbill";
    public static final String WEIXIN_GET_OPEN_ID = "https://api.weixin.qq.com/sns/oauth2/access_token?";//appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
    public static final String WEIXIN_GET_JSAPI_TICKE = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?";//access_token=ACCESS_TOKEN&type=jsapi

    /**
     * 根据微信配置的类型获取对应的微信访问接口，默认会缓存2小时。
     *
     * @param configType 配置类型：用户公众号、技师公众号，@see io.dabing.o2o.entity.config.WeixinConfig
     * @return 调用微信所有接口之前的必备参数accessToken
     */
    @Cacheable(value = "default", key = "#configType")
    @Override
    public String getAccessToken(String configType) {
        String url = WEIXIN_ACCESS_TOKEN + "&grant_type=client_credential&appid=" + wxAppId + "&secret=" + wxAppSecret;
        try {
            logger.info("请求accesstoken url:{}", url);
            String retval = HttpClientUtil.get(url, 30000, 30000, "UTF-8");
            logger.info("获取accesstoken返回值为：{}", retval);
            Map<String, String> json = JSON.parseObject(retval, HashMap.class);
            String errcode = json.get("errcode");
            if (errcode != null) {
                logger.info("获取accesstoken异常,errorCode:{},errmsg:{}", errcode, json.get("errmsg"));
                return null;
            }
            return json.get("access_token");
        } catch (Exception e) {
            logger.error("获取accesstoken异常", e);
        }

        return null;
    }

    /**
     * 根据accessToken获取jsapi_ticket值，用于页面调用jssdk时签名使用。必须缓存
     *
     * @param accessToken
     * @return
     */
    @Cacheable(value = "default", key = "#accessToken")
    @Override
    public String getJSApiTicket(String accessToken) {
        String url = WEIXIN_GET_JSAPI_TICKE + "&access_token=" + accessToken + "&type=jsapi";
        try {
            logger.info("请求jsapi_ticket url:{}", url);
            String retval = HttpClientUtil.get(url, 30000, 30000, "UTF-8");
            logger.info("获取jsapi_ticket返回值为：{}", retval);
            Map<String, String> json = JSON.parseObject(retval, HashMap.class);
            String errmsg = json.get("errmsg");
            if (!errmsg.equals("ok")) {
                logger.info("获取jsapi_ticket异常,errmsg:{},errorCode:{}", errmsg, json.get("errcode"));
                return null;
            }
            return json.get("ticket");
        } catch (Exception e) {
            logger.error("获取jsapi_ticket异常", e);
        }

        return null;
    }
    @Override
    public WXRedPackResp sendRedPack(WXRedPackReq req) {
        WXRedPackResp resp = new WXRedPackResp();
        resp.setResultCode(WXRefundOrderResp.CODE_0);
        FileInputStream p12CertStream = null;
        KeyStore keyStore;
        try {
            String wxCertFile = wxCertPath + wxMchId + ".p12";
            p12CertStream = new FileInputStream(new File(wxCertFile));
            keyStore = KeyStore.getInstance("PKCS12");
            keyStore.load(p12CertStream, wxMchId.toCharArray());
        } catch (Exception e) {
            resp.setErrorMsg("证书获取失败");
            resp.setResultCode(WXRefundOrderResp.CODE_1);
            logger.error("证书获取失败：{}", e.getMessage(), e);
            return resp;
        } finally {
            if (p12CertStream != null) {
                try {
                    p12CertStream.close();
                } catch (IOException e) {
                    logger.error("证书文件流关闭失败", e);
                }
            }
        }
        try {
            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, wxMchId.toCharArray()).build();
            SSLConnectionSocketFactory ssslCSF = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(ssslCSF).build();
            HttpPost httpPost = new HttpPost(WEIXIN_RED_PACK);
            StringEntity myEntity = new StringEntity(convertRedPackReq(req), "UTF-8");
            httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");
            httpPost.setEntity(myEntity);
            logger.info("executing request" + httpPost.getRequestLine());
            CloseableHttpResponse response = httpclient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            logger.info("----------------------------------------");
            logger.info(response.getStatusLine().toString());
            if (entity != null) {
                logger.info("Response content length: " + entity.getContentLength());
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
                StringBuffer contentSB = new StringBuffer(bufferedReader.readLine());
                String readContentLine;
                while ((readContentLine = bufferedReader.readLine()) != null) {
                    contentSB.append(readContentLine);
                }
                Map<String, String> map = XMLUtil.doXMLParse(contentSB.toString());
                String return_code = map.get("return_code");
                if (!"SUCCESS".equals(return_code)) {
                    resp.setErrorMsg(map.get("return_msg"));
                    resp.setResultCode(WXRedPackResp.CODE_1);
                    return resp;
                }
                String err_code_des = map.get("err_code_des");
                if (!StringUtils.isEmpty(err_code_des)) {
                    resp.setErrorMsg(err_code_des);
                    resp.setResultCode(WXRedPackResp.CODE_1);
                    return resp;
                }
            }
        } catch (Exception e) {
            logger.error("调用微信发送红包接口异常", e);
            resp.setResultCode(WXRedPackResp.CODE_1);
            resp.setErrorMsg("调用微信发送红包失败");
            return resp;
        }
        return resp;
    }


    private String convertRedPackReq(WXRedPackReq req) {
        SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
        /** MD5进行签名，必须为UTF-8编码，注意上面几个参数名称的大小写 */
        /** 商户订单号（每个订单号必须唯一） 组成：mch_id+yyyymmdd+10位一天内不能重复的数字。	 */
        parameters.put("mch_billno", wxMchId + req.getOrderNo());
        /** 商户号 */
        parameters.put("mch_id", wxMchId);
        /** 公众账号appid */
        parameters.put("wxappid", wxAppId);
        /** 商户名称  */
        parameters.put("send_name", req.getSendName());
        /** 用户openid */
        parameters.put("re_openid", req.getReOpenid());
        /** 付款金额 付款金额，单位分 */
        parameters.put("total_amount", req.getAmount());
        /** 发放个数*/
        parameters.put("total_num", 1);
        /** 红包祝福语*/
        parameters.put("wishing", req.getWishing());
        /** Ip地址 调用接口的机器Ip地址 */
        parameters.put("client_ip", "192.168.1.100");
        /** 活动名称 */
        parameters.put("act_name", req.getActName());
        /** 备注 */
        parameters.put("remark", req.getRemark());
        /** 随机字符串	 */
        parameters.put("nonce_str", WXUtil.getNonceStr());
        /** 场景id	PRODUCT_2:抽奖 发放红包使用场景，红包金额大于200时必传 */
        parameters.put("scene_id", "PRODUCT_2");

        String sign = WXUtil.createSign("UTF-8", parameters, wxAppKey);
        parameters.put("sign", sign);
        /** 生成xml结构的数据，用于统一下单接口的请求 */
        String requestXML = WXUtil.getRequestXml(parameters);
        return requestXML;
    }

    @Override
    public WXRefundOrderResp refundOrder(WXRefundOrderReq req) {
        WXRefundOrderResp resp = new WXRefundOrderResp();
        resp.setResultCode(WXRefundOrderResp.CODE_0);
        String appMchId = wxMchId;
        FileInputStream p12CertStream = null;
        KeyStore keyStore;
        try {
            String wxCertFile = wxCertPath + appMchId + ".p12";
            p12CertStream = new FileInputStream(new File(wxCertFile));
            keyStore = KeyStore.getInstance("PKCS12");
            keyStore.load(p12CertStream, appMchId.toCharArray());
        } catch (Exception e) {
            logger.error("证书获取失败：{}", e.getMessage(), e);
            resp.setResultCode(WXRefundOrderResp.CODE_1);
            resp.setErrorMsg("证书获取失败");
            return resp;
        } finally {
            if (p12CertStream != null) {
                try {
                    p12CertStream.close();
                } catch (IOException e) {
                    logger.error("证书文件流关闭失败", e);
                }
            }
        }
        try {
            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, appMchId.toCharArray()).build();
            SSLConnectionSocketFactory ssslCSF = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(ssslCSF).build();
            HttpPost httpPost = new HttpPost(WEIXIN_REFUND_ORDER);
            StringEntity myEntity = new StringEntity(convertRefundReq(req), "UTF-8");
            httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");
            httpPost.setEntity(myEntity);
            logger.info("executing request" + httpPost.getRequestLine());
            CloseableHttpResponse response = httpclient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            logger.info("----------------------------------------");
            logger.info(response.getStatusLine().toString());
            if (entity != null) {
                logger.info("Response content length: " + entity.getContentLength());
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
                StringBuffer contentSB = new StringBuffer(bufferedReader.readLine());
                String readContentLine;
                while ((readContentLine = bufferedReader.readLine()) != null) {
                    contentSB.append(readContentLine);
                }
                Map<String, String> map = XMLUtil.doXMLParse(contentSB.toString());
                String return_code = map.get("return_code");
                if (!"SUCCESS".equals(return_code)) {
                    resp.setResultCode(WXRefundOrderResp.CODE_1);
                    resp.setErrorMsg(map.get("return_msg"));
                    return resp;
                }
                String err_code_des = map.get("err_code_des");
                if (!StringUtils.isEmpty(err_code_des)) {
                    resp.setResultCode(WXRefundOrderResp.CODE_1);
                    resp.setErrorMsg(err_code_des);
                    return resp;
                }
            }
        } catch (Exception e) {
            logger.error("调用微信退款异常", e);
            resp.setResultCode(WXRefundOrderResp.CODE_1);
            resp.setErrorMsg("调用微信退款失败");
            return resp;
        }
        return resp;
    }

    private String convertRefundReq(WXRefundOrderReq req) {
        SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
        /** 公众号APPID */
        parameters.put("appid", wxAppId);
        /** 商户号 */
        parameters.put("mch_id", wxMchId);
        /** 操作员 */
        parameters.put("op_user_id", wxMchId);
        /** 随机字符串 */
        parameters.put("nonce_str", WXUtil.getNonceStr());
        /** 商户系统内部的退款单号，商户系统内部唯一，同一退款单号多次请求只退一笔	 */
        parameters.put("out_refund_no", req.getOrderNo());
        /** 商户侧传给微信的订单号*/
        parameters.put("out_trade_no", req.getOrderNo());
        /** 退款总金额，订单总金额，单位为分，只能为整数 */
        parameters.put("refund_fee", req.getOrderPrice());
        /** 订单金额以分为单位，只能为整数 */
        parameters.put("total_fee", req.getOrderPrice());
        /** 微信订单号 微信生成的订单号，在支付通知中有返回 */
        parameters.put("transaction_id", req.getTransactionId());

        /** 退款资金来源 仅针对老资金流商户使用 REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款（默认使用未结算资金退款）
         * REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款(限非当日交易订单的退款） */
        if (req.isNotSettle()) {
            parameters.put("refund_account", "REFUND_SOURCE_UNSETTLED_FUNDS");
        } else {
            parameters.put("refund_account", "REFUND_SOURCE_RECHARGE_FUNDS");

        }

        /** MD5进行签名，必须为UTF-8编码，注意上面几个参数名称的大小写 */
        String sign = WXUtil.createSign("UTF-8", parameters, wxAppKey);
        parameters.put("sign", sign);
        /** 生成xml结构的数据，用于统一下单接口的请求 */
        String requestXML = WXUtil.getRequestXml(parameters);
        return requestXML;
    }


    /**
     * 调用微信统一下单接口，返回前端需要的字段
     *
     * @param req 支付请求参数
     */
    @Override
    public WXUnifiedOderResp unifiedOrder(WXUnifiedOderReq req) {
        SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
        /** 公众号APPID */
        parameters.put("appid", wxAppId);
        /** 商户号 */
        parameters.put("mch_id", wxMchId);
        /** 随机字符串 */
        parameters.put("nonce_str", WXUtil.getNonceStr());
        /** 商品名称 */
        parameters.put("body", req.getProductName());

        /** 订单号 */
        parameters.put("out_trade_no", req.getOrderNo());

        /** 订单金额以分为单位，只能为整数 */
        parameters.put("total_fee", req.getOrderPrice());
        /** 客户端本地ip */
        parameters.put("spbill_create_ip", req.getCustomerIp());
        /** 支付回调地址 */
        parameters.put("notify_url", wxPayReturnUrl);
        /** 支付方式为JSAPI支付 */
        parameters.put("trade_type", "JSAPI");
        /** 用户微信的openid，当trade_type为JSAPI的时候，该属性字段必须设置 */
        parameters.put("openid", req.getCustomerOpenId());
        /* 附加参数数据 */
        parameters.put("attach", req.getAttach());

        /** MD5进行签名，必须为UTF-8编码，注意上面几个参数名称的大小写 */
        String sign = WXUtil.createSign("UTF-8", parameters, wxAppKey);
        parameters.put("sign", sign);

        /** 生成xml结构的数据，用于统一下单接口的请求 */
        String requestXML = WXUtil.getRequestXml(parameters);
        logger.info("调用微信统一下单支付 requestXML：{}", requestXML);
        try {
            String result = HttpClientUtil.postXml(WEIXIN_PAY_UNIFIED_ORDER, requestXML);
            logger.info("调用微信统一下单接口返回报文:{}", result);
            WXUnifiedOderResp resp = new WXUnifiedOderResp();
            /** 解析微信返回的信息，以Map形式存储便于取值 */
            Map<String, String> map = XMLUtil.doXMLParse(result);
            String return_code = map.get("return_code");
            if (!"SUCCESS".equals(return_code)) {
                resp.setErrorMsg(map.get("return_msg"));
                return resp;
            }
            String err_code_des = map.get("err_code_des");
            if (!StringUtils.isEmpty(err_code_des)) {
                resp.setErrorMsg(err_code_des);
                return resp;
            }
            String nocestr = WXUtil.getNonceStr();
            resp.setAppId(wxAppId);
            resp.setNonceStr(nocestr);
            /**
             * 获取预支付单号prepay_id后，需要将它参与签名。
             * 微信支付最新接口中，要求package的值的固定格式为prepay_id=...
             */
            resp.setPackageValue("prepay_id=" + map.get("prepay_id"));
            resp.setSignType("MD5");

            SortedMap<Object, Object> params = new TreeMap<Object, Object>();
            String timeStamp = WXUtil.getTimeStamp();
            params.put("appId", wxAppId);
            params.put("nonceStr", nocestr);
            /** 预支付单号 */
            params.put("package", "prepay_id=" + map.get("prepay_id"));
            /** 微信支付新版本签名算法使用MD5，不是SHA1 */
            params.put("signType", "MD5");
            params.put("timeStamp", timeStamp);


            /**
             * 获取预支付prepay_id之后，需要再次进行签名，参与签名的参数有：appId、nonceStr、package、signType、timeStamp.
             * 主意上面参数名称的大小写和先后顺序
             * 该签名用于前端js中WeixinJSBridge.invoke中的paySign的参数值
             */
            String paySign = WXUtil.createSign("UTF-8", params, wxAppKey);
            resp.setPaySign(paySign);
            resp.setTimestamp(timeStamp);
            return resp;

        } catch (Exception e) {
            logger.error("调用微信统一下单支付异常", e);
        }

        return null;
    }

    /**
     * 通过code获取openId
     *
     * @param code       由微信静默方式授权提供的code值
     * @param configType 公众号类型
     * @return
     */
    @Override
    public String getOpenIdByCode(String code, String configType) {
        //https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
        String url = WEIXIN_GET_OPEN_ID + "appId=" + wxAppId + "&secret=" + wxAppSecret + "&code=" + code + "&grant_type=authorization_code";
        try {
            logger.info("get openId url:{}", url);
            String retval = HttpClientUtil.get(url, 30000, 30000, "UTF-8");
            logger.info("get openId retval:{}", retval);
            Map<String, String> map = JSON.parseObject(retval, HashMap.class);
            if (map.get("errcode") != null) {
                logger.error("获取openId异常，retval:{}", retval);
            } else {
                return map.get("openid");
            }
        } catch (Exception e) {
            logger.error("获取openId异常,configType:{}", configType, e);
        }
        return null;
    }
    @Override
    public WeiXinDownloadBillResp downloadBill(WeiXinDownloadBillReq req) {
        SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
        /** 公众号APPID */
        parameters.put("appid", wxAppId);
        /** 商户号 */
        parameters.put("mch_id", wxMchId);
        /** 随机字符串 */
        parameters.put("nonce_str", WXUtil.getNonceStr());
        /** 对账单日期 */
        parameters.put("bill_date", req.getBillDate());

        /** 账单类型 */
        parameters.put("bill_type", req.getBillType());

        /** MD5进行签名，必须为UTF-8编码，注意上面几个参数名称的大小写 */
        String sign = WXUtil.createSign("UTF-8", parameters, wxAppKey);
        parameters.put("sign", sign);

        /** 生成下载对账单的xml结构的清数据 */
        String requestXML = WXUtil.getRequestXml(parameters);
        logger.info("调用微信下载对账单接口 requestXML：{}", requestXML);
        WeiXinDownloadBillResp resp = new WeiXinDownloadBillResp();
        try {
            String result = HttpClientUtil.postXml(WEIXIN_DOWNLOAD_BILL, requestXML);
            logger.info("调用微信下载对账单接口 responseXMl：{}", result);
            /** 解析微信返回的信息，以Map形式存储便于取值 */
            if (result.contains("return_code") && result.contains("return_msg")) {
                Map<String, String> map = XMLUtil.doXMLParse(result);
                resp.setReturnCode(WeiXinDownloadBillResp.CODE_FAIL);
                resp.setReturnMsg(map.get("return_msg"));
            } else {
                resp.setReturnMsg(result);
            }
        } catch (Exception e) {
            logger.error("调用微信下载对账单接口异常", e);
            resp.setReturnCode(WeiXinDownloadBillResp.CODE_FAIL);
            resp.setReturnMsg("调用微信下载对账单接口出现异常！");
        }
        return resp;
    }


    /**
     * 发送微信消息
     *
     * @param accessToken 微信接口必须
     * @param message     消息对象
     */
    @Override
    public Map<String, String> doSendMessage(String accessToken, WeiXinMessage message) {
        Map<String, String> mapResp = new HashMap<>();
        mapResp.put("FLAG", "OK");
        mapResp.put("MSG", "发送成功！");
        if (StringUtils.isEmpty(message.getTouser())) {
            logger.info(" openId 为空，不发送微信消息 touser:{}", message.getTouser());
            mapResp.put("FLAG", "WRONG");
            mapResp.put("MSG", "openID 为空！");
            return mapResp;
        }
        String json = JSON.toJSONString(message);
        try {
            logger.info("微信消息推送 请求报文 touser:{},requestJson:{}", message.getTouser(), json);
            String retval = HttpClientUtil.postJSON(WEIXIN_SEND_MESSAGE + accessToken, json);
            logger.info("微信消息推送 返回报文 touser:{},retval:{}", message.getTouser(), retval);
            Map<String, Object> map = JSON.parseObject(retval, HashMap.class);
            String errmsg = (String) map.get("errmsg");
            if (!"ok".equals(errmsg)) {
                mapResp.put("FLAG", map.get("errcode").toString());
                mapResp.put("MSG", map.get("errmsg").toString());
            }
        } catch (Exception e) {
            logger.error("发送微信消息异常!", e);
            mapResp.put("FLAG", "error");
            mapResp.put("MSG", e.getMessage());
        }
        return mapResp;
    }


    public static void main(String[] args) {
        //get token
        String accessToken = "";
        String appid = "wxbc330df0e013e079";
        String appsecret = "1d5a471f4b31f030ff4f02c95baa7e94";
        String templateId = "UJ1pvvB1DFAVwLyJWfnLCm3dEhZ-pq0X03BBtxm_0_E";
        String url = WEIXIN_ACCESS_TOKEN + "&grant_type=client_credential&appid=" + appid + "&secret=" + appsecret;
        try {
            logger.info("请求accesstoken url:{}", url);
            String retval = HttpClientUtil.get(url, 30000, 30000, "UTF-8");
            logger.info("获取accesstoken返回值为：{}", retval);
            Map<String, String> json = JSON.parseObject(retval, HashMap.class);
            String errcode = json.get("errcode");
            if (errcode != null) {
                logger.info("获取accesstoken异常,errorCode:{},errmsg:{}", errcode, json.get("errmsg"));
            }
            accessToken = json.get("access_token");
        } catch (Exception e) {
            logger.error("获取accesstoken异常", e);
        }

        /**
         *
         项目名称：长安福特单次基础保养（机油+机滤）
         订单金额：1元
         请您移步618欢乐送活动页面，刷新后进行订单支付，有效时间截止今日12点。感谢您对我们活动的支持，祝您生活愉快！
         */
        List<String[]> openIds = new ArrayList();
        List<String> errorList = new ArrayList<>();
        for (String[] data : openIds) {
            String openId = data[0];
            String orderNo = data[1];
            String mobile = data[2];
            try {
                WeiXinMessage message = new WeiXinMessage();
                message.setTemplate_id(templateId);
                message.setTouser(openId);
                message.setUrl("http://www.changanfordwechat.com/fordwechat/control?state=17259");
                Map<String, MessageData> messageData = new HashMap<>();
                messageData.put("first", new MessageData("尊敬的客户您好，您参与618欢乐送活动秒杀到的单次保养已生效，有效期至2017年12月31日。", null));
                messageData.put("keyword1", new MessageData(orderNo, null));
                messageData.put("keyword2", new MessageData(mobile, null));
                messageData.put("remark", new MessageData("请您保留支付凭证至全国任意长安福特授权经销商处使用。如您已使用，请忽略本条信息。感谢您对我们活动的支持！", null));
                message.setData(messageData);
                //doSendMessage(accessToken, message);
            } catch (Exception e) {
                errorList.add(openId);
            }
        }
        logger.info("异常openId:"+JSON.toJSONString(errorList));
    }
}
